#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <functional>

#include <pthread.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"
#include "ThreadPool.hpp"

using namespace LogModule;
using namespace ThreadPoolModule;

using task_t = std::function<void()>;
using handler_t = std::function<std::string(std::string&)>;

#define BACKLOG 8

uint16_t gport = 8080;

class TcpServer
{
public:
    TcpServer(handler_t handler, int port = gport)
        : _handler(handler), _port(port), _isrunning(false)
    {}

    ~TcpServer() {}

    void InitServer()
    {
        // 1. 创建TCP套接字
        _listensockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_listensockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error";
            Die(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "socket create success, listensockfd is: " << _listensockfd;

        // 2. 绑定
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = ::htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = ::bind(_listensockfd, CONV(&local), sizeof(local));
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error";
            Die(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success";

        // 3. Tcp是面向连接的, 就要求Tcp随时随地等待被客户端连接, Tcp需要将socket设置为监听状态
        //    客户端请求连接时: 将客户端连接请求放入一个监听队列中
        n = ::listen(_listensockfd, BACKLOG);
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "listen error";
            Die(LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "listen success";

        // ::signal(SIGCHLD, SIG_IGN); // 子进程退出, 父进程无需wait, 操作系统自动回收资源
    }

    // Tcp也是全双工的: 在同一个文件描述符中, 既可以读又可以写
    void HandlerRequest(int sockfd)
    {
        LOG(LogLevel::INFO) << "开始处理客户端请求...";
        char inbuffer[4096];
        std::string package;
        while (true)
        {
            // 约定: 客户端发过来的是一条完整的命令string

            // 1. 读取客户端发送来的消息
            ssize_t n = ::recv(sockfd, inbuffer, sizeof(inbuffer) - 1, 0); // 读取是不完善的
            if (n > 0) 
            {
                inbuffer[n] = 0;
                package += inbuffer; // len\r\n{json}\r\n

                // 保证了报文的完整性
                std::string reault = _handler(package);
                if(reault.empty()) continue;

                // 2. 向客户端发送消息
                ::send(sockfd, reault.c_str(), reault.size(), 0); // 写入也是不完善的
            }
            else if (n == 0)
            {
                // read如果读取的返回值是0, 表示客户端退出
                LOG(LogLevel::INFO) << "客户端退出: " << sockfd;
                break;
            }
            else
            {
                // 读取失败
                break;
            }
        }
        ::close(sockfd); // 关闭fd, 防止fd泄漏问题
    }

    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // 1. Tcp不能直接获取数据: 需要获取新连接
            // 阻塞等待, 直到有客户端连接请求进入监听队列, 然后从队列中取出一个请求, 为该客户端建立连接,
            // 并返回一个新的套接字描述符, 通过这个新的套接字描述符就可以与客户端进行数据的发送和接收
            struct sockaddr_in peer;
            socklen_t peerlen = sizeof(peer);
            int sockfd = ::accept(_listensockfd, CONV(&peer), &peerlen);
            if (sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept error: " << strerror(errno);
                continue;
            }
            LOG(LogLevel::INFO) << "accept success, sockfd is: " << sockfd;

            // 获取客户端的信息: IP + 端口号
            InetAddr addr(peer);
            LOG(LogLevel::INFO) << "client info: " << addr.Addr();

            // version1->单进程版本: 单客户端访问
            // HandlerRequest(sockfd);

            // version2->多进程版本: 多客户端访问
            // pid_t id = fork();
            // if (id == 0)
            // {
            //     // 子进程: 继承父进程的文件描述符表, 有两张表
            //     ::close(_listensockfd); // 关闭不需要的文件描述符: 监听套接字

            //     if(fork() > 0) exit(0); // 子进程退出

            //     // 孙子进程->孤儿进程: 不断与客户端的数据传输, 退出后被操作系统自动回收
            //     HandlerRequest(sockfd);

            //     exit(0);
            // }
            // // 父进程: 不断与客户端建立连接
            // ::close(sockfd); // 关闭不需要的文件描述符: socket

            // // waitpid不会阻塞
            // int rid = ::waitpid(id, nullptr, 0);
            // if(rid < 0)
            // {
            //     LOG(LogLevel::WARNING) << "waitpid error";
            // }

            // version3->多线程版本: 多客户端访问
            // 主线程和新线程共享同一张文件描述符表
            // pthread_t tid;
            // ThreadData *data = new ThreadData();
            // data->sockfd = sockfd;
            // data->self = this;
            // pthread_create(&tid, nullptr, ThreadEntry, (void *)data);

            // version4->线程池版本: 多客户端访问(适合处理短任务)
            task_t task = std::bind(&TcpServer::HandlerRequest, this, sockfd); // 构建任务
            ThreadPool<task_t>::GetInstance()->Equeue(task);

            // ThreadPool<task_t>::GetInstance()->Equeue([this, &sockfd](){
            //     this->HandlerRequest(sockfd);
            // });
        }
    }

    struct ThreadData
    {
        int sockfd;
        TcpServer *self;
    };

    // 类中ThreadEntry函数带有this指针, 需要加上static
    // 而没有this指针, 又无法调用HandlerReques函数
    // 解决方法: 封装ThreadData结构体
    static void *ThreadEntry(void *argc)
    {
        pthread_detach(pthread_self()); // 线程分离: 线程退出时由操作系统自动回收, 防止类似僵尸进程的问题
        ThreadData *data = (ThreadData *)argc;
        data->self->HandlerRequest(data->sockfd);
        return nullptr;
    }

    void Stop()
    {
        _isrunning = false;
    }

private:
    int _listensockfd; // 监听套接字
    uint16_t _port;
    bool _isrunning;

    handler_t _handler; // 处理客户端发来的任务
};